Todo:
- add columns for totals for each pollutant/sector
# load libraries and read data
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
-- Attaching packages ----------------------------------------------------------------------------------------------------------- tidyverse 1.3.0 --
v ggplot2 3.3.2 v purrr 0.3.4
v tibble 3.1.0 v dplyr 1.0.2
v tidyr 1.1.2 v stringr 1.4.0
v readr 1.4.0 v forcats 0.5.0
package 㤼㸱tibble㤼㸲 was built under R version 4.0.4-- Conflicts -------------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
library(readxl)
library(plotly)
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5
Attaching package: 㤼㸱plotly㤼㸲
The following object is masked from 㤼㸱package:ggplot2㤼㸲:
last_plot
The following object is masked from 㤼㸱package:stats㤼㸲:
filter
The following object is masked from 㤼㸱package:graphics㤼㸲:
layout
ghg_emissions_clean <- read_csv("data/clean_data/ghg_emissions.csv")
-- Column specification ----------------------------------------------------------------------------------------------------------------------------
cols(
ccp_mapping = col_character(),
source_name = col_character(),
pollutant = col_character(),
year = col_double(),
value = col_double(),
units = col_character()
)
summarised_data <- emissions_data %>%
group_and_summarise(ccp_mapping, pollutant)
create_bar_plot <-function(df = summarised_data) {
x_name <- names(df)[1]
df %>%
ggplot() +
aes(x = x_name, y = value, fill = x_name) +
geom_col(position = "stack", show.legend = FALSE)
}
test_function <- function(df, var) {
# convert snakecase variable name to title for ui
varname <- str_to_title(str_replace_all(var, "_", " "))
}
build_slider_input <- function(df, varname) {
rng <- range(df, na.rm = TRUE)
sliderInput(var,
paste0(varname, " Range:"),
min = rng[1],
max = rng[2],
value = rng,
sep = "",
step = 1,
ticks = FALSE)
}
test_function(df = emissions_data, var = "pollutant")
[1] "pickerInput"
substr("jewel", 2, nchar("jewel"))
[1] "ewel"
test <- "ccp_mapping\",\"pollutant"
eval(parse(text=gsub("\\", "", deparse(test), fixed=TRUE)))
Error in parse(text = gsub("\\", "", deparse(test), fixed = TRUE)) :
<text>:1:14: unexpected ','
1: "ccp_mapping",
^
test_function <- function(df, var) {
# convert snakecase variable name to title for ui
varname <- str_to_title(str_replace_all(var, "_", " "))
print(dropdown_lookup[[var]])
}
emissions_data %>%
group_by_("ccp_mapping", "pollutant") %>%
summarise(value = sum(value, na.rm = TRUE))
`summarise()` regrouping output by 'ccp_mapping' (override with `.groups` argument)
emissions_data %>%
group_by(ccp_mapping, pollutant) %>%
summarise(value = sum(value, na.rm = TRUE), .groups = 'drop_last') %>%
ggplot() +
aes(x = names(emissions_data)[1], y = value) +
geom_col()

emissions_data <- read_csv("data/clean_data/ghg_emissions.csv")
-- Column specification ------------------------------------------------------------------------------------------------------------------------
cols(
ccp_mapping = col_character(),
source_name = col_character(),
pollutant = col_character(),
year = col_double(),
value = col_double(),
units = col_character()
)
emissions_data %>%
group_by(year) %>%
summarise(value = sum(value, na.rm = TRUE)) %>%
ggplot() +
aes(x = year,
y = value) +
geom_line() +
ylim(0, NA)
`summarise()` ungrouping output (override with `.groups` argument)

Only emissions - > emissions that are greater than 0
ghg_true_emissions <- ghg_emissions_clean %>%
filter(year == max(ghg_emissions_clean$year)) %>%
filter(value >= 0) %>%
mutate(across(where(is.character), ~str_to_title(.)))
# need to split by pollutant and year
ghg_emissions_clean %>%
select(ccp_mapping, source_name) %>%
filter(str_detect(source_name, paste("^", ccp_mapping, sep = ""))) %>%
unique()
ghg_wide_emissions <- ghg_wide %>%
filter(!value < 0)
ghg_wide_sinks <- ghg_wide %>%
filter(value < 0)
get_child_cols <- function(df, additional_vars, standard_vars = c("value", "units")) {
temp <- names(df)
remove <- c(additional_vars, standard_vars)
child_cols <- temp [!temp %in% remove]
}
child_cols <- get_child_cols(ghg_wide_emissions, additional_vars)
child_cols
[1] "child_order_0" "child_order_1" "child_order_2" "child_order_3"
previous_children
NULL
create_child_table(ghg_wide_emissions,
current_child = "child_order_0",
previous_children = NULL,
additional_vars = c("pollutant", "year"))
Error: Problem with `mutate()` input `..1`.
x Input `..1` must be a vector, not a `quosure/formula` object.
i Input `..1` is `current_child`.
Run `rlang::last_error()` to see where the error occurred.
previous_children <- NULL
children <- c(previous_children, "child_order_0")
children
[1] "child_order_0"
road_traffic <- read_csv("data/clean_data/transport/road_traffic.csv")
-- Column specification -----------------------------------------------------------------------------------------------------------------------
cols(
road_type = col_character(),
vehicle_type = col_character(),
year = col_double(),
vehicle_kilometers_millions = col_double()
)
road_traffic %>%
filter(road_type == "Major roads (M and A)") %>%
filter(vehicle_type == "Cars") %>%
ggplot() +
aes(x = .data[["year"]], y = value) +
geom_line() +
xlim(min(year), max(.x)) +
ylim(0, NA)
Error in limits(c(...), "x") : object 'year' not found
create_child_table <- function(df, current_child, previous_children, additional_vars) {
current_child <- quo(current_child)
previous_children <- quo(previous_children)
additional_vars <- quo(additional_vars)
df %>%
group_by(!!current_child, rev(!!previous_children), !!additional_vars) %>%
summarise(value = sum(value), .groups = 'drop') %>%
unite(!!previous_children, col = "parent", sep = " - ") %>%
unite(c(!!previous_children, !!current_child), sep = " - ") %>%
select(id, label = !!current_child, parent, !!additional_vars, value)
}
names(ghg_wide_emissions) %in% "child_order_[0-9]"
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
temp <- names(ghg_wide_emissions)
remove <- c(additional_vars, "value", "units")
temp [!temp %in% remove]
[1] "child_order_0" "child_order_1" "child_order_2" "child_order_3"
TODO::
dashboard - review page dashboard - transport explorer
create_child_table(ghg_wide_emissions, current_child = "child_order_0",
previous_children = "", additional_vars = c("pollutant", "year"))
Error: Invalid index: out of bounds
create_child_table(ghg_wide_emissions, current_child = child_order_0,
previous_children = NULL, additional_vars = c(pollutant, year))
Error: Problem with `mutate()` input `..1`.
x Input `..1` must be a vector, not a `quosure/formula` object.
i Input `..1` is `current_child`.
Run `rlang::last_error()` to see where the error occurred.
if (is.null(previous_children)) {
print("bean")
}
[1] "bean"
vector <- c("child_1", "child_2")
paste(paste(vector, collapse = " - "),"child_3", sep = " - ")
[1] "child_1 - child_2 - child_3"
df %>% group_by(current_child, rev(previous_children), additional_vars) %>% summarise(value = sum(value), .groups = “drop”) %>% mutate(id = paste(previous_children, current_child, sep = " - “), parent = paste(previous_children, sep =” - ")) %>% select(id, label = current_child, parent, additional_vars, value)
vector <- c()
paste(paste(vector, sep = " - "),"child_3", sep = " - ")
[1] " - child_3"
vector <- c()
tibble(
parent = c("bean", "curd", "whey", "sprout", ""),
people = c("david", "sasha", "john", "smith", "delilah"),
shapes = c("triangle", "rectangle", "square", "diamond", "")
) %>%
unite(c(1,2,3), col = "id", sep = " - ", remove = FALSE)
tibble(
parent = c("dad", "bean", "", "fava"),
child = c("")
) %>%
select(child) %>%
pull()
[1] "" "" "" ""
# get sector totals for parent df (top level)
ghg_sector_totals <- ghg_true_emissions %>%
group_by(ccp_mapping) %>%
summarise(sector_total = sum(value), .groups = "drop_last")
parent_df <- ghg_sector_totals %>%
mutate(parent = "") %>%
select(label = ccp_mapping,
parent,
value = sector_total)
# the rest of the data
children_df <- ghg_true_emissions %>%
filter(year == max(ghg_true_emissions$year)) %>%
group_by(source_name, ccp_mapping) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
select(label = source_name,
parent = ccp_mapping,
value)
# combine into one hierarchical df, create id column
emissions_long <- bind_rows(list(parent_df, children_df)) %>%
mutate(id = paste(parent, label, sep = " - "), .before = 'label') %>%
mutate(id = str_remove(id, "^ - "))
Other method - seperate column
parents <- emissions_wide %>%
filter(is.na(second)) %>%
select(id, label, parent, value)
first_born <- emissions_wide %>%
filter(!is.na(second)) %>%
group_by(second, first) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
mutate(id = paste(first, second, sep = " - ")) %>%
ungroup() %>%
select(id,
label = second,
parent = first,
value)
second_born <- emissions_wide %>%
filter(!is.na(third)) %>%
group_by(third, second, first) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
mutate(id = paste(first, second, third, sep = " - ")) %>%
mutate(parent = paste(first, second, sep = " - ")) %>%
ungroup() %>%
select(id,
label = third,
parent,
value)
third_born <- emissions_wide %>%
filter(!is.na(fourth)) %>%
group_by(fourth, third, second, first) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
mutate(id = paste(first, second, third, fourth, sep = " - ")) %>%
mutate(parent = paste(first, second, third, sep = " - ")) %>%
ungroup() %>%
select(id,
label = fourth,
parent,
value)
n_potential_child_layers <- emissions_long %>%
select(id) %>%
pull() %>%
str_count(" - ") %>%
max()
# the number of potential child layers is the maximum included in the dataset
n_potential_child_layers <- emissions_long %>%
select(id) %>%
pull() %>%
str_count(" - ") %>%
max()
create_hierarchy_df <- function(df, id_col = "id", id_sep = " - ") {
n_potential_child_layers <- df %>%
select(col) %>%
pull() %>%
str_count(sep) %>%
max()
}
fig <- plot_ly()
fig <- fig %>%
add_trace(
name = "Emissions",
ids = emissions$id,
labels = emissions$label,
parents = emissions$parent,
values = emissions$value,
text = emissions$units,
type = 'sunburst',
maxdepth = 2,
domain = list(column = 0),
branchvalues = 'total',
insidetextorientation = 'radial',
marker=list(colorscale='Viridis'),
text = ~units,
textinfo='label+percent root+value',
hoverinfo = paste("%{label}: <br>%{value}",'text')
)
fig <- fig %>%
add_trace(
name = "Sinks",
ids = sinks$id,
labels = sinks$label,
parents = sinks$parent,
values = sinks$value,
text = sinks$units,
type = 'sunburst',
maxdepth = 2,
domain = list(column = 1),
branchvalues = 'total',
insidetextorientation = 'radial',
marker=list(colorscale='Viridis'),
text = ~units,
textinfo='label+percent root+value',
hoverinfo = paste("%{label}: <br>%{value}",'text')
)
fig <- fig %>%
layout(
grid = list(columns =2, rows = 1),
margin = list(l = 0, r = 0, b = 0, t = 0))
fig
tibble(
labels = c("Eve", "Seth", "Enos", "Noam", "Awan", "Enoch"),
parents = c("", "Eve", "Seth", "Seth", "Eve", "Awan"),
values = c(16, 12, 10, 2, 4, 4)
)
fig <- plot_ly(
labels = cain_ble$labels,
parents = cain_ble$parents,
values = cain_ble$values,
type = 'sunburst'
)
fig
emissions_long$label[1:10]
[1] "Agriculture" "Electricity Generation" "Industry" "Land use" "Residential"
[6] "Services" "Transport" "Waste" "Accidental fires" "Accidental fires"
d <- data.frame(
ids = c(
"North America", "Europe", "Australia", "North America - Football", "Soccer",
"North America - Rugby", "Europe - Football", "Rugby",
"Europe - American Football","Australia - Football", "Association",
"Australian Rules", "Autstralia - American Football", "Australia - Rugby",
"Rugby League", "Rugby Union"
),
labels = c(
"North<br>America", "Europe", "Australia", "Football", "Soccer", "Rugby",
"Football", "Rugby", "American<br>Football", "Football", "Association",
"Australian<br>Rules", "American<br>Football", "Rugby", "Rugby<br>League",
"Rugby<br>Union"
),
parents = c(
"", "", "", "North America", "North America", "North America", "Europe",
"Europe", "Europe","Australia", "Australia - Football", "Australia - Football",
"Australia - Football", "Australia - Football", "Australia - Rugby",
"Australia - Rugby"
),
stringsAsFactors = FALSE
)
fig <- plot_ly(d, ids = ~ids, labels = ~labels, parents = ~parents, type = 'sunburst')
d
class(emissions_long)
[1] "tbl_df" "tbl" "data.frame"
class(cain_ble)
[1] "tbl_df" "tbl" "data.frame"
class(cain_ble)
diff_order <- ghg_emissions_clean %>%
dplyr::group_by(ccp_mapping, year, pollutant) %>%
dplyr::summarise(value = sum(value), units = units[1], .groups = "keep") %>%
filter(pollutant == "CO2") %>%
filter(year == min(ghg_emissions_clean$year) |
year == max(ghg_emissions_clean$year)) %>%
ungroup() %>%
group_by(ccp_mapping) %>%
mutate(diff = lag(value) - value) %>%
arrange(diff) %>%
drop_na() %>%
select(ccp_mapping) %>%
pull()
ghg_emissions_clean %>%
dplyr::group_by(ccp_mapping, year, pollutant) %>%
dplyr::summarise(value = sum(value), units = units[1], .groups = "keep") %>%
filter(pollutant == "CO2") %>%
mutate(ccp_mapping = factor(ccp_mapping, levels = rev(diff_order))) %>%
ggplot() +
aes(x = year, y = value, fill = ccp_mapping) +
geom_area() +
facet_wrap(~pollutant) +
theme_bw()
ghg_emissions_clean %>%
group_by(pollutant) %>%
summarise(emissions = sum(value))
plotly::ggplotly(
ghg_emissions_clean %>%
filter(year == 2018) %>%
filter(pollutant %in% c("CO2", "CH4")) %>%
ggplot() +
aes(x = factor(ccp_mapping, levels = rev(levels(factor(ccp_mapping)))),
y = value, fill = source_name,
text = paste0('</br> Sector: ', ccp_mapping,
'</br> Emissions: ', value,
'</br> Source Name: ', source_name)) +
geom_col(position = "stack") +
theme_bw() +
theme(legend.position = "none") +
labs(x = "Sector",
y = paste0("Emissions (", ghg_emissions_clean$units[1], ")")) +
coord_flip(),
tooltip = 'text'
)
ghg_emissions_data %>%
names()
ghg_emissions_data %>%
select()
input <- list()
input$col_choice = "national_communication_categories"
ghg_emissions_clean %>%
group_by_(input$col_choice, "emission_year") %>%
summarise(total_ghg_emissions = sum(emissions)) %>%
ggplot() +
aes(x = EmissionYear, y = total_ghg_emissions, group = `National Communication Categories`, colour = `National Communication Categories`) +
geom_line() +
geom_point() +
scale_x_continuous(breaks = seq(1990,2020,5)) +
theme(legend.position = 0)
ghg_emissions_data %>%
distinct(`National Communication Categories`)
ghg_emissions_data %>%
filter(EmissionYear != "BaseYear") %>%
mutate(EmissionYear = as.numeric(EmissionYear)) %>%
group_by(EmissionYear) %>%
summarise(total_ghg_emissions = sum(`Emissions (MtCO2e)`)) %>%
ggplot() +
aes(x = EmissionYear, y = total_ghg_emissions) +
geom_line() +
geom_point() +
scale_x_continuous(breaks = seq(1990,2020,5)) +
ylim(0, 80) +
theme(legend.position = 0) +
theme_bw()
ghg_emissions_data %>%
distinct(`CCP mapping`)
ghg_emissions_data %>%
filter(EmissionYear != "BaseYear") %>%
mutate(EmissionYear = as.numeric(EmissionYear)) %>%
group_by(`CCP mapping`, EmissionYear) %>%
summarise(total_ghg_emissions = sum(`Emissions (MtCO2e)`)) %>%
ggplot() +
aes(x = EmissionYear, y = total_ghg_emissions, group = `CCP mapping`, colour = `CCP mapping`) +
geom_line() +
geom_point() +
scale_x_continuous(breaks = seq(1990,2020,5))
`summarise()` regrouping output by 'CCP mapping' (override with `.groups` argument)

ghg_emissions_data %>%
filter(EmissionYear != "BaseYear") %>%
mutate(EmissionYear = as.numeric(EmissionYear)) %>%
filter(`National Communication Categories` != `CCP mapping`) %>%
select(`National Communication Categories`, `CCP mapping`) %>%
unique()
emissions_sankey <- emissions_data %>%
select(ccp_mapping, source_name, pollutant, emission_year, emissions, units)
category_id,category_name,subcategory_id,subcategory_name,year,emissions,emission
filtered_df <- ghg_emissions_clean %>%
select(ccp_mapping, source_name, pollutant, emission_year, emissions, units) %>%
filter(pollutant == "CO2") %>%
filter(emission_year == "2005")
total_emissions_for_gas <- filtered_df %>%
summarise(sum(emissions)) %>%
pull()
total_emissions_by_category <- filtered_df %>%
select(-pollutant) %>%
group_by(ccp_mapping) %>%
summarise(cat_sum = sum(emissions), .groups = 'drop_last')
categories <- filtered_df %>%
distinct(ccp_mapping) %>%
pull()
n_categories <- length(categories)
sources <- filtered_df %>%
distinct(source_name) %>%
pull()
n_sources <- length(sources)
n_sources
[1] 159
node_names <- c("Total", categories, sources, "Other")
node_names_df <- data.frame("name" = node_names)
total_sankey_tibble <- total_emissions_by_category %>%
mutate(total = "Total") %>%
mutate(total = match(total, node_names) -1) %>%
mutate(ccp_mapping = match(ccp_mapping, node_names) -1) %>%
select(source = total,
target = ccp_mapping,
value = cat_sum)
total_filtered_emissions <- total_sankey_tibble %>%
summarise(sum(value)) %>%
pull()
other_emissions <- total_emissions_for_gas - total_filtered_emissions
total_other_sankey_tibble <- tibble(
"source" = c(0),
"target" = (match("Other", node_names) -1),
"value" = c(other_emissions)
)
sub_sankey_tibble <- filtered_df %>%
select(- c(units, pollutant, emission_year)) %>%
mutate(ccp_mapping = match(ccp_mapping, node_names) -1,
source_name = match(source_name, node_names) -1)
names(sub_sankey_tibble) = c("source", "target", "value")
sankey_tibble <- total_sankey_tibble %>%
bind_rows(sub_sankey_tibble) %>%
bind_rows(total_other_sankey_tibble)
links_matrix <- data.frame(as.matrix(sankey_tibble, byrow = TRUE, ncols = 3))
# Add a 'group' column to each connection:
links <- links_matrix %>%
mutate(group = case_when(
source == 0 ~ paste("type_", target, sep = ""),
source!=0 ~ paste("type_", source, sep = "")
))
nodes <- node_names_df
# Add a 'group' column to each node.
# All of them in the same group to make them the same colour
nodes$group <- as.factor(c("my_unique_group"))
emissions <- list()
emissions$nodes <- nodes
emissions$links <- links
node_names <- c("Total", categories, sources, "Other")
node_names_df <- data.frame("name" = node_names)
total_sankey_tibble <- total_emissions_by_category %>%
mutate(total = "Total") %>%
mutate(total = match(total, node_names) -1) %>%
mutate(ccp_mapping = match(ccp_mapping, node_names) -1) %>%
select(source = total,
target = ccp_mapping,
value = cat_sum)
total_filtered_emissions <- total_sankey_tibble %>%
summarise(sum(value)) %>%
pull()
other_emissions <- total_emissions_for_gas - total_filtered_emissions
total_other_sankey_tibble <- tibble(
"source" = c(0),
"target" = (match("Other", node_names) -1),
"value" = c(other_emissions)
)
sub_sankey_tibble <- filtered_df %>%
select(- c(units, pollutant, emission_year)) %>%
mutate(ccp_mapping = match(ccp_mapping, node_names) -1,
source_name = match(source_name, node_names) -1)
names(sub_sankey_tibble) = c("source", "target", "value")
sankey_tibble <- total_sankey_tibble %>%
bind_rows(sub_sankey_tibble) %>%
bind_rows(total_other_sankey_tibble)
links_matrix <- data.frame(as.matrix(sankey_tibble, byrow = TRUE, ncols = 3))
# Add a 'group' column to each connection:
links <- links_matrix %>%
mutate(group = case_when(
source == 0 ~ paste("type_", target, sep = ""),
source!=0 ~ paste("type_", source, sep = "")
))
nodes <- node_names_df
# Add a 'group' column to each node.
# All of them in the same group to make them the same colour
nodes$group <- as.factor(c("my_unique_group"))
emissions <- list()
emissions$nodes <- nodes
emissions$links <- links
total_filtered_emissions <- total_sankey_tibble %>%
summarise(sum(value)) %>%
pull()
other_emissions <- total_emissions_for_gas - total_filtered_emissions
total_other_sankey_tibble <- tibble(
"source" = c(0),
"target" = (match("Other", node_names) -1),
"value" = c(other_emissions)
)
sub_sankey_tibble <- filtered_tibble %>%
select(-category_id, -subcategory_id, -year) %>%
mutate(category_name = match(category_name, node_names) -1,
subcategory_name = match(subcategory_name, node_names) -1)
names(sub_sankey_tibble) = c("source", "target", "value")
sankey_tibble <- total_sankey_tibble %>%
bind_rows(sub_sankey_tibble) %>%
bind_rows(total_other_sankey_tibble)
links_matrix <- data.frame(as.matrix(sankey_tibble, byrow = TRUE, ncols = 3))
# Add a 'group' column to each connection:
links <- links_matrix %>%
mutate(group = case_when(
source == 0 ~ paste("type_", target, sep = ""),
source!=0 ~ paste("type_", source, sep = "")
))
nodes <- node_names_df
# Add a 'group' column to each node.
# All of them in the same group to make them the same colour
nodes$group <- as.factor(c("my_unique_group"))
emissions <- list()
emissions$nodes <- nodes
emissions$links <- links
make_sankey_dfs <- function(data, userYear, userGas) {
n_categories <- data %>%
distinct(category_name) %>%
nrow()
total_emissions_for_gas <- data %>%
filter(emission == userGas()) %>%
filter(year == userYear()) %>%
summarise(sum(emissions)) %>%
pull()
filtered_tibble <- data %>%
filter(emission == userGas()) %>%
select(-emission) %>%
filter(year == userYear()) %>%
filter(emissions > userResolution())
total_emissions_by_cat <- filtered_tibble %>%
group_by(category_name) %>%
summarise(cat_sum = sum(emissions), .groups = 'drop_last')
categories <- filtered_tibble %>%
distinct(category_name) %>%
pull()
subcategories <- filtered_tibble %>%
distinct(subcategory_name) %>%
pull()
node_names <- c("Total", categories, subcategories, "Other")
node_names_df <- data.frame("name" = node_names)
total_sankey_tibble <- total_emissions_by_cat %>%
mutate(total = "Total") %>%
mutate(total = match(total, node_names) -1) %>%
mutate(category_name = match(category_name, node_names) -1) %>%
select(source = total,
target = category_name,
value = cat_sum)
total_filtered_emissions <- total_sankey_tibble %>%
summarise(sum(value)) %>%
pull()
other_emissions <- total_emissions_for_gas - total_filtered_emissions
total_other_sankey_tibble <- tibble(
"source" = c(0),
"target" = (match("Other", node_names) -1),
"value" = c(other_emissions)
)
sub_sankey_tibble <- filtered_tibble %>%
select(-category_id, -subcategory_id, -year) %>%
mutate(category_name = match(category_name, node_names) -1,
subcategory_name = match(subcategory_name, node_names) -1)
names(sub_sankey_tibble) = c("source", "target", "value")
sankey_tibble <- total_sankey_tibble %>%
bind_rows(sub_sankey_tibble) %>%
bind_rows(total_other_sankey_tibble)
links_matrix <- data.frame(as.matrix(sankey_tibble, byrow = TRUE, ncols = 3))
# Add a 'group' column to each connection:
links <- links_matrix %>%
mutate(group = case_when(
source == 0 ~ paste("type_", target, sep = ""),
source!=0 ~ paste("type_", source, sep = "")
))
nodes <- node_names_df
# Add a 'group' column to each node.
# All of them in the same group to make them the same colour
nodes$group <- as.factor(c("my_unique_group"))
emissions <- list()
emissions$nodes <- nodes
emissions$links <- links
return(emissions)
}
make_sankey_dfs(emissions_sankey, userYear = 2005, userGas = "CH4", userResolution = 50)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KIyBUb2RvOg0KDQotIGFkZCBjb2x1bW5zIGZvciB0b3RhbHMgZm9yIGVhY2ggcG9sbHV0YW50L3NlY3Rvcg0KDQpgYGB7cn0NCiMgbG9hZCBsaWJyYXJpZXMgYW5kIHJlYWQgZGF0YQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkocGxvdGx5KQ0KDQpnaGdfZW1pc3Npb25zX2NsZWFuIDwtIHJlYWRfY3N2KCJkYXRhL2NsZWFuX2RhdGEvZ2hnX2VtaXNzaW9ucy5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0KDQpgYGANCg0KYGBge3J9DQpzdW1tYXJpc2VkX2RhdGEgPC0gZW1pc3Npb25zX2RhdGEgJT4lIA0KICBncm91cF9hbmRfc3VtbWFyaXNlKGNjcF9tYXBwaW5nLCBwb2xsdXRhbnQpDQpgYGANCg0KYGBge3J9DQpjcmVhdGVfYmFyX3Bsb3QgPC1mdW5jdGlvbihkZiA9IHN1bW1hcmlzZWRfZGF0YSkgew0KICB4X25hbWUgPC0gbmFtZXMoZGYpWzFdDQogIA0KDQp9DQpgYGANCg0KDQoNCmBgYHtyfQ0KdGVzdF9mdW5jdGlvbiA8LSBmdW5jdGlvbihkZiwgdmFyKSB7DQogICMgY29udmVydCBzbmFrZWNhc2UgdmFyaWFibGUgbmFtZSB0byB0aXRsZSBmb3IgdWkNCiAgdmFybmFtZSA8LSBzdHJfdG9fdGl0bGUoc3RyX3JlcGxhY2VfYWxsKHZhciwgIl8iLCAiICIpKQ0KICANCn0NCmBgYA0KDQoNCmBgYHtyfQ0KDQpgYGANCg0KDQpgYGB7cn0NCmJ1aWxkX3NsaWRlcl9pbnB1dCA8LSBmdW5jdGlvbihkZiwgdmFybmFtZSkgew0KICBybmcgPC0gcmFuZ2UoZGYsIG5hLnJtID0gVFJVRSkNCiAgDQogIHNsaWRlcklucHV0KHZhciwNCiAgICAgICAgICAgICAgcGFzdGUwKHZhcm5hbWUsICIgUmFuZ2U6IiksDQogICAgICAgICAgICAgIG1pbiA9IHJuZ1sxXSwNCiAgICAgICAgICAgICAgbWF4ID0gcm5nWzJdLA0KICAgICAgICAgICAgICB2YWx1ZSA9IHJuZywNCiAgICAgICAgICAgICAgc2VwID0gIiIsDQogICAgICAgICAgICAgIHN0ZXAgPSAxLA0KICAgICAgICAgICAgICB0aWNrcyA9IEZBTFNFKQ0KfQ0KICANCmBgYA0KDQpgYGB7cn0NCnRlc3RfZnVuY3Rpb24oZGYgPSBlbWlzc2lvbnNfZGF0YSwgdmFyID0gInBvbGx1dGFudCIpDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc3Vic3RyKCJqZXdlbCIsIDIsIG5jaGFyKCJqZXdlbCIpKQ0KYGBgDQoNCg0KYGBge3J9DQp0ZXN0IDwtICJjY3BfbWFwcGluZ1wiLFwicG9sbHV0YW50Ig0KDQpldmFsKHBhcnNlKHRleHQ9Z3N1YigiXFwiLCAiIiwgZGVwYXJzZSh0ZXN0KSwgZml4ZWQ9VFJVRSkpKQ0KYGBgDQpgYGB7cn0NCnBhc3RlMChjKCJmaXJzdCIsICJzZWNvbmQiKSwgY29sbGFwc2UgPSAiLCIpDQpgYGANCg0KDQpgYGB7cn0NCmVtaXNzaW9uc19kYXRhICU+JSANCiAgZ3JvdXBfYnlfKCJjY3BfbWFwcGluZyIsICJwb2xsdXRhbnQiKSAlPiUgDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSwgbmEucm0gPSBUUlVFKSkNCmBgYA0KDQoNCg0KYGBge3J9DQplbWlzc2lvbnNfZGF0YSAlPiUgDQogIGdyb3VwX2J5KGNjcF9tYXBwaW5nLCBwb2xsdXRhbnQpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlLCBuYS5ybSA9IFRSVUUpLCAuZ3JvdXBzID0gJ2Ryb3BfbGFzdCcpICU+JSANCiAgZ2dwbG90KCkgKw0KICBhZXMoeCA9IG5hbWVzKClbMV0sIHkgPSB2YWx1ZSkgKw0KICBnZW9tX2NvbCgpDQpgYGANCmBgYHtyfQ0KDQpgYGANCg0KDQpgYGB7cn0NCmVtaXNzaW9uc19kYXRhIDwtIHJlYWRfY3N2KCJkYXRhL2NsZWFuX2RhdGEvZ2hnX2VtaXNzaW9ucy5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0KZW1pc3Npb25zX2RhdGEgJT4lDQogIGdyb3VwX2J5KHllYXIpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIGdncGxvdCgpICsNCiAgICBhZXMoeCA9IHllYXIsDQogICAgICAgIHkgPSB2YWx1ZSkgKw0KICBnZW9tX2xpbmUoKSArDQogIHlsaW0oMCwgTkEpDQpgYGANCg0KDQpPbmx5IGVtaXNzaW9ucyAtID4gZW1pc3Npb25zIHRoYXQgYXJlIGdyZWF0ZXIgdGhhbiAwDQoNCmBgYHtyfQ0KZ2hnX3RydWVfZW1pc3Npb25zIDwtIGdoZ19lbWlzc2lvbnNfY2xlYW4gJT4lIA0KICBmaWx0ZXIoeWVhciA9PSBtYXgoZ2hnX2VtaXNzaW9uc19jbGVhbiR5ZWFyKSkgJT4lIA0KICBmaWx0ZXIodmFsdWUgPj0gMCkgJT4lIA0KICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIH5zdHJfdG9fdGl0bGUoLikpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBuZWVkIHRvIHNwbGl0IGJ5IHBvbGx1dGFudCBhbmQgeWVhcg0KZ2hnX2VtaXNzaW9uc19jbGVhbiAlPiUgDQogIHNlbGVjdChjY3BfbWFwcGluZywgc291cmNlX25hbWUpICU+JSANCiAgZmlsdGVyKHN0cl9kZXRlY3Qoc291cmNlX25hbWUsIHBhc3RlKCJeIiwgY2NwX21hcHBpbmcsIHNlcCA9ICIiKSkpICU+JSANCiAgdW5pcXVlKCkNCmBgYA0KDQpgYGB7cn0NCiMgdmFyX25hbWVzDQoNCg0KYGBgDQoNCmBgYHtyfQ0KDQpnZXRfY2hpbGRfY29scyA8LSBmdW5jdGlvbihkZiwgYWRkaXRpb25hbF92YXJzLCBzdGFuZGFyZF92YXJzID0gYygidmFsdWUiLCAidW5pdHMiKSkgew0KICB0ZW1wIDwtIG5hbWVzKGRmKQ0KICByZW1vdmUgPC0gYyhhZGRpdGlvbmFsX3ZhcnMsIHN0YW5kYXJkX3ZhcnMpDQogIA0KICBjaGlsZF9jb2xzIDwtIHRlbXAgWyF0ZW1wICVpbiUgcmVtb3ZlXQ0KfQ0KDQoNCmNoaWxkX2NvbHMgPC0gZ2V0X2NoaWxkX2NvbHMoZ2hnX3dpZGVfZW1pc3Npb25zLCBhZGRpdGlvbmFsX3ZhcnMpDQoNCmNoaWxkX2NvbHMNCmBgYA0KDQpgYGB7cn0NCmNoaWxkX2NvbHMgPC0gYygiY2hpbGRfb3JkZXJfMCIsIGNoaWxkX25hbWVzKQ0KDQpwcmV2aW91c19jaGlsZHJlbiA8LSBOVUxMDQoNCmhpZXJhcmNoeV9kZiA8LSB0aWJibGUoKQ0KDQpmb3IgKGNoaWxkIGluIGNoaWxkX2NvbHMpIHsNCiAgcHJpbnQoY2hpbGQpDQogIGludGVybWVkaWF0ZV90aWJibGUgPC0gY3JlYXRlX2NoaWxkX3RhYmxlKGdoZ193aWRlX2VtaXNzaW9ucywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudF9jaGlsZCA9IGNoaWxkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2aW91c19jaGlsZHJlbiA9IHByZXZpb3VzX2NoaWxkcmVuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZGRpdGlvbmFsX3ZhcnMgPSBjKCJwb2xsdXRhbnQiLCAieWVhciIpKQ0KICBwcmV2aW91c19jaGlsZHJlbiA8LSBjKHByZXZpb3VzX2NoaWxkcmVuLCBjaGlsZCkNCiAgaGllcmFyY2h5X2RmICU+JSANCiAgICBiaW5kX3Jvd3MoaW50ZXJtZWRpYXRlX3RpYmJsZSkNCn0NCg0KaGllcmFyY2h5X2RmDQpgYGANCg0KDQoNCmBgYHtyfQ0KDQpgYGANCg0KDQoNCg0KDQpgYGB7cn0NCmNyZWF0ZV9jaGlsZF90YWJsZShnaGdfd2lkZV9lbWlzc2lvbnMsDQogICAgICAgICAgICAgICAgICAgY3VycmVudF9jaGlsZCA9ICJjaGlsZF9vcmRlcl8wIiwNCiAgICAgICAgICAgICAgICAgICBwcmV2aW91c19jaGlsZHJlbiA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgYWRkaXRpb25hbF92YXJzID0gYygicG9sbHV0YW50IiwgInllYXIiKSkNCmBgYA0KDQoNCmBgYHtyfQ0KcHJldmlvdXNfY2hpbGRyZW4gPC0gTlVMTA0KDQpjaGlsZHJlbiA8LSBjKHByZXZpb3VzX2NoaWxkcmVuLCAiY2hpbGRfb3JkZXJfMCIpDQoNCmNoaWxkcmVuDQpgYGANCg0KDQoNCmBgYHtyfQ0KDQpgYGANCmBgYHtyfQ0Kcm9hZF90cmFmZmljIDwtIHJlYWRfY3N2KCJkYXRhL2NsZWFuX2RhdGEvdHJhbnNwb3J0L3JvYWRfdHJhZmZpYy5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0Kcm9hZF90cmFmZmljICU+JQ0KICBtdXRhdGUodmVoaWNsZV90eXBlID0gaWZlbHNlKHN0cl9kZXRlY3QodmVoaWNsZV90eXBlLCAiXkFsbCB0cmFmZmljIiksICJBbGwgVHJhZmZpYyIsIHN0cl90b190aXRsZSh2ZWhpY2xlX3R5cGUpKSkgJT4lIA0KICBtdXRhdGUodW5pdHMgPSAiVmVoaWNsZSBLaWxvbWV0ZXJzIChNaWxsaW9ucykiKSAlPiUgDQogIHJlbmFtZSh2YWx1ZSA9IHZlaGljbGVfa2lsb21ldGVyc19taWxsaW9ucykgJT4lIA0KICB3cml0ZV9jc3YoImRhdGEvY2xlYW5fZGF0YS90cmFuc3BvcnQvcm9hZF90cmFmZmljLmNzdiIpDQogIA0KYGBgDQoNCg0KYGBge3J9DQoNCg0Kcm9hZF90cmFmZmljICU+JSANCiAgZmlsdGVyKHJvYWRfdHlwZSA9PSAiTWFqb3Igcm9hZHMgKE0gYW5kIEEpIikgJT4lIA0KICBmaWx0ZXIodmVoaWNsZV90eXBlID09ICJDYXJzIikgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGFlcyh4ID0gLmRhdGFbWyJ5ZWFyIl1dLCB5ID0gdmFsdWUpICsNCiAgZ2VvbV9saW5lKCkgKw0KICB4bGltKG1pbih5ZWFyKSwgbWF4KC54KSkgKw0KICB5bGltKDAsIE5BKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmNyZWF0ZV9jaGlsZF90YWJsZSA8LSBmdW5jdGlvbihkZiwgY3VycmVudF9jaGlsZCwgcHJldmlvdXNfY2hpbGRyZW4sIGFkZGl0aW9uYWxfdmFycykgew0KICBoaWVyYXJjaHlfZGYgPC0gZ2hnX3dpZGVfZW1pc3Npb25zICU+JSANCiAgICBncm91cF9ieV8oLmRvdHMgPSBjKGN1cnJlbnRfY2hpbGQsDQogICAgICAgICAgICAgICAgICAgICAgICByZXYocHJldmlvdXNfY2hpbGRyZW4pLA0KICAgICAgICAgICAgICAgICAgICAgICAgYWRkaXRpb25hbF92YXJzKSkgJT4lIA0KICAgIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSksIC5ncm91cHMgPSAnZHJvcCcpDQogIA0KICBpZiAoaXMubnVsbChwcmV2aW91c19jaGlsZHJlbikpIHsNCiAgICBoaWVyYXJjaHlfZGYgJT4lDQogICAgICBtdXRhdGUocGFyZW50ID0gIiIpICU+JSANCiAgICAgIHNlbGVjdChpZCA9IGN1cnJlbnRfY2hpbGQsIGxhYmVsID0gY3VycmVudF9jaGlsZCwNCiAgICAgICAgICAgICBwYXJlbnQsIGFkZGl0aW9uYWxfdmFycywgdmFsdWUpDQogIH0gZWxzZSB7DQogICAgaGllcmFyY2h5X2RmICU+JSANCiAgICAgIHVuaXRlKHByZXZpb3VzX2NoaWxkcmVuLCBjb2wgPSAicGFyZW50Iiwgc2VwID0gIiAtICIsIHJlbW92ZSA9IEZBTFNFKSAlPiUgDQogICAgICB1bml0ZShjKHByZXZpb3VzX2NoaWxkcmVuLCBjdXJyZW50X2NoaWxkKSwNCiAgICAgICAgICAgIGNvbCA9ICJpZCIsIHNlcCA9ICIgLSAiLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICAgICAgc2VsZWN0KGlkLCBsYWJlbCA9IGN1cnJlbnRfY2hpbGQsIHBhcmVudCwgYWRkaXRpb25hbF92YXJzLCB2YWx1ZSkNCiAgfQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQpuYW1lcyhnaGdfd2lkZV9lbWlzc2lvbnMpICVpbiUgImNoaWxkX29yZGVyX1swLTldIg0KICANCnRlbXAgPC0gbmFtZXMoZ2hnX3dpZGVfZW1pc3Npb25zKQ0KcmVtb3ZlIDwtIGMoYWRkaXRpb25hbF92YXJzLCAidmFsdWUiLCAidW5pdHMiKQ0KDQoNCnRlbXAgWyF0ZW1wICVpbiUgcmVtb3ZlXQ0KYGBgDQoNClRPRE86Og0KDQpkYXNoYm9hcmQgLSByZXZpZXcgcGFnZQ0KZGFzaGJvYXJkIC0gdHJhbnNwb3J0IGV4cGxvcmVyDQoNCg0KYGBge3J9DQpoZWxwKG1hZ3JpdHRyKQ0Kc2BgYA0KDQoNCmBgYHtyfQ0KY3JlYXRlX2NoaWxkX3RhYmxlKGdoZ193aWRlX2VtaXNzaW9ucywgY3VycmVudF9jaGlsZCA9ICJjaGlsZF9vcmRlcl8wIiwNCiAgICAgICAgICAgICAgICAgICBwcmV2aW91c19jaGlsZHJlbiA9ICIiLCBhZGRpdGlvbmFsX3ZhcnMgPSBjKCJwb2xsdXRhbnQiLCAieWVhciIpKQ0KYGBgDQoNCg0KYGBge3J9DQpjcmVhdGVfY2hpbGRfdGFibGUoZ2hnX3dpZGVfZW1pc3Npb25zLCBjdXJyZW50X2NoaWxkID0gY2hpbGRfb3JkZXJfMCwNCiAgICAgICAgICAgICAgICAgICBwcmV2aW91c19jaGlsZHJlbiA9IE5VTEwsIGFkZGl0aW9uYWxfdmFycyA9IGMocG9sbHV0YW50LCB5ZWFyKSkNCmBgYA0KDQpgYGB7cn0NCmN1cnJlbnRfY2hpbGQgPC0gImNoaWxkX29yZGVyXzEiDQpwcmV2aW91c19jaGlsZHJlbiA8LSAiY2hpbGRfb3JkZXJfMCINCmFkZGl0aW9uYWxfdmFycyA8LSBjKCJwb2xsdXRhbnQiLCAieWVhciIpDQoNCmhpZXJhcmNoeV9kZiA8LSBnaGdfd2lkZV9lbWlzc2lvbnMgJT4lIA0KICBncm91cF9ieV8oLmRvdHMgPSBjKGN1cnJlbnRfY2hpbGQsIHJldihwcmV2aW91c19jaGlsZHJlbiksIGFkZGl0aW9uYWxfdmFycykpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgLmdyb3VwcyA9ICdkcm9wJykNCg0KaWYgKGlzLm51bGwocHJldmlvdXNfY2hpbGRyZW4pKSB7DQogIGhpZXJhcmNoeV9kZiAlPiUNCiAgICBtdXRhdGUocGFyZW50ID0gIiIpICU+JSANCiAgICBzZWxlY3QoaWQgPSBjdXJyZW50X2NoaWxkLCBsYWJlbCA9IGN1cnJlbnRfY2hpbGQsIHBhcmVudCwgYWRkaXRpb25hbF92YXJzLCB2YWx1ZSkNCn0gZWxzZSB7DQogIGhpZXJhcmNoeV9kZiAlPiUgDQogICAgdW5pdGUocHJldmlvdXNfY2hpbGRyZW4sIGNvbCA9ICJwYXJlbnQiLCBzZXAgPSAiIC0gIiwgcmVtb3ZlID0gRkFMU0UpICU+JSANCiAgICB1bml0ZShjKHByZXZpb3VzX2NoaWxkcmVuLCBjdXJyZW50X2NoaWxkKSwgY29sID0gImlkIiwgc2VwID0gIiAtICIsIHJlbW92ZSA9IEZBTFNFKSAlPiUgDQogICAgc2VsZWN0KGlkLCBsYWJlbCA9IGN1cnJlbnRfY2hpbGQsIHBhcmVudCwgYWRkaXRpb25hbF92YXJzLCB2YWx1ZSkNCn0NCiAgDQpgYGANCg0KYGBge3J9DQppZiAoaXMubnVsbChwcmV2aW91c19jaGlsZHJlbikpIHsNCiAgcHJpbnQoImJlYW4iKQ0KfQ0KYGBgDQoNCg0KDQoNCmRmICU+JQ0KICBncm91cF9ieShjdXJyZW50X2NoaWxkLCByZXYocHJldmlvdXNfY2hpbGRyZW4pLCBhZGRpdGlvbmFsX3ZhcnMpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lIA0KICBtdXRhdGUoaWQgPSBwYXN0ZShwcmV2aW91c19jaGlsZHJlbiwgY3VycmVudF9jaGlsZCwgc2VwID0gIiAtICIpLA0KICAgICAgICAgcGFyZW50ID0gcGFzdGUocHJldmlvdXNfY2hpbGRyZW4sIHNlcCA9ICIgLSAiKSkgJT4lIA0KICBzZWxlY3QoaWQsIGxhYmVsID0gY3VycmVudF9jaGlsZCwgcGFyZW50LCBhZGRpdGlvbmFsX3ZhcnMsIHZhbHVlKQ0KDQpgYGB7cn0NCnZlY3RvciA8LSBjKCJjaGlsZF8xIiwgImNoaWxkXzIiKQ0KDQpwYXN0ZShwYXN0ZSh2ZWN0b3IsIGNvbGxhcHNlID0gIiAtICIpLCJjaGlsZF8zIiwgc2VwID0gIiAtICIpDQpgYGANCg0KYGBge3J9DQp2ZWN0b3IgPC0gYygpDQoNCnBhc3RlKHBhc3RlKHZlY3Rvciwgc2VwID0gIiAtICIpLCJjaGlsZF8zIiwgc2VwID0gIiAtICIpDQpgYGANCg0KDQpgYGB7cn0NCnZlY3RvciA8LSBjKCkNCg0KdGliYmxlKA0KICBwYXJlbnQgPSBjKCJiZWFuIiwgImN1cmQiLCAid2hleSIsICJzcHJvdXQiLCAiIiksDQogIHBlb3BsZSA9IGMoImRhdmlkIiwgInNhc2hhIiwgImpvaG4iLCAic21pdGgiLCAiZGVsaWxhaCIpLA0KICBzaGFwZXMgPSBjKCJ0cmlhbmdsZSIsICJyZWN0YW5nbGUiLCAic3F1YXJlIiwgImRpYW1vbmQiLCAiIikNCikgJT4lIA0KICB1bml0ZShjKDEsMiwzKSwgY29sID0gImlkIiwgc2VwID0gIiAtICIsIHJlbW92ZSA9IEZBTFNFKQ0KYGBgDQoNCg0KYGBge3J9DQp0aWJibGUoDQogIHBhcmVudCA9IGMoImRhZCIsICJiZWFuIiwgIiIsICJmYXZhIiksDQogIGNoaWxkID0gYygiIikNCikgJT4lIA0KICBzZWxlY3QoY2hpbGQpICU+JSANCiAgcHVsbCgpDQpgYGANCg0KDQpgYGB7cn0NCiMgZ2V0IHNlY3RvciB0b3RhbHMgZm9yIHBhcmVudCBkZiAodG9wIGxldmVsKQ0KDQpnaGdfc2VjdG9yX3RvdGFscyA8LSBnaGdfdHJ1ZV9lbWlzc2lvbnMgJT4lIA0KICBncm91cF9ieShjY3BfbWFwcGluZykgJT4lIA0KICBzdW1tYXJpc2Uoc2VjdG9yX3RvdGFsID0gc3VtKHZhbHVlKSwgLmdyb3VwcyA9ICJkcm9wX2xhc3QiKQ0KDQpwYXJlbnRfZGYgPC0gZ2hnX3NlY3Rvcl90b3RhbHMgJT4lIA0KICBtdXRhdGUocGFyZW50ID0gIiIpICU+JSANCiAgc2VsZWN0KGxhYmVsID0gY2NwX21hcHBpbmcsDQogICAgICAgICBwYXJlbnQsDQogICAgICAgICB2YWx1ZSA9IHNlY3Rvcl90b3RhbCkNCg0KIyB0aGUgcmVzdCBvZiB0aGUgZGF0YQ0KDQpjaGlsZHJlbl9kZiA8LSBnaGdfdHJ1ZV9lbWlzc2lvbnMgJT4lIA0KICBmaWx0ZXIoeWVhciA9PSBtYXgoZ2hnX3RydWVfZW1pc3Npb25zJHllYXIpKSAlPiUgDQogIGdyb3VwX2J5KHNvdXJjZV9uYW1lLCBjY3BfbWFwcGluZykgJT4lIA0KICBzdW1tYXJpc2UodmFsdWUgPSBzdW0odmFsdWUpLCAuZ3JvdXBzID0gImRyb3BfbGFzdCIpICU+JSANCiAgc2VsZWN0KGxhYmVsID0gc291cmNlX25hbWUsDQogICAgICAgICBwYXJlbnQgPSBjY3BfbWFwcGluZywNCiAgICAgICAgIHZhbHVlKQ0KDQojIGNvbWJpbmUgaW50byBvbmUgaGllcmFyY2hpY2FsIGRmLCBjcmVhdGUgaWQgY29sdW1uDQoNCmVtaXNzaW9uc19sb25nIDwtIGJpbmRfcm93cyhsaXN0KHBhcmVudF9kZiwgY2hpbGRyZW5fZGYpKSAlPiUgDQogIG11dGF0ZShpZCA9IHBhc3RlKHBhcmVudCwgbGFiZWwsIHNlcCA9ICIgLSAiKSwgLmJlZm9yZSA9ICdsYWJlbCcpICU+JSANCiAgbXV0YXRlKGlkID0gc3RyX3JlbW92ZShpZCwgIl4gLSAiKSkNCmBgYA0KDQojIE90aGVyIG1ldGhvZCAtIHNlcGVyYXRlIGNvbHVtbg0KDQpgYGB7cn0NCmVtaXNzaW9uc193aWRlIDwtIGVtaXNzaW9uc19sb25nICU+JSANCiAgc2VwYXJhdGUoaWQsICIgLSAiLCBpbnRvID0gYygiZmlyc3QiLCAic2Vjb25kIiwgInRoaXJkIiwgImZvdXJ0aCIpLA0KICAgICAgICAgICByZW1vdmUgPSBGQUxTRSwgZXh0cmEgPSAibWVyZ2UiLCBmaWxsID0gInJpZ2h0IikNCmBgYA0KDQpgYGB7cn0NCnBhcmVudHMgPC0gZW1pc3Npb25zX3dpZGUgJT4lIA0KICBmaWx0ZXIoaXMubmEoc2Vjb25kKSkgJT4lIA0KICBzZWxlY3QoaWQsIGxhYmVsLCBwYXJlbnQsIHZhbHVlKQ0KYGBgDQoNCmBgYHtyfQ0KZmlyc3RfYm9ybiA8LSBlbWlzc2lvbnNfd2lkZSAlPiUgDQogIGZpbHRlcighaXMubmEoc2Vjb25kKSkgJT4lIA0KICBncm91cF9ieShzZWNvbmQsIGZpcnN0KSAlPiUgDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSksIC5ncm91cHMgPSAiZHJvcF9sYXN0IikgJT4lIA0KICBtdXRhdGUoaWQgPSBwYXN0ZShmaXJzdCwgc2Vjb25kLCBzZXAgPSAiIC0gIikpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgc2VsZWN0KGlkLA0KICAgICAgICAgbGFiZWwgPSBzZWNvbmQsDQogICAgICAgICBwYXJlbnQgPSBmaXJzdCwNCiAgICAgICAgIHZhbHVlKQ0KYGBgDQoNCmBgYHtyfQ0Kc2Vjb25kX2Jvcm4gPC0gZW1pc3Npb25zX3dpZGUgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKHRoaXJkKSkgJT4lIA0KICBncm91cF9ieSh0aGlyZCwgc2Vjb25kLCBmaXJzdCkgJT4lIA0KICBzdW1tYXJpc2UodmFsdWUgPSBzdW0odmFsdWUpLCAuZ3JvdXBzID0gImRyb3BfbGFzdCIpICU+JSANCiAgbXV0YXRlKGlkID0gcGFzdGUoZmlyc3QsIHNlY29uZCwgdGhpcmQsIHNlcCA9ICIgLSAiKSkgJT4lIA0KICBtdXRhdGUocGFyZW50ID0gcGFzdGUoZmlyc3QsIHNlY29uZCwgc2VwID0gIiAtICIpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIHNlbGVjdChpZCwNCiAgICAgICAgIGxhYmVsID0gdGhpcmQsDQogICAgICAgICBwYXJlbnQsDQogICAgICAgICB2YWx1ZSkNCmBgYA0KDQpgYGB7cn0NCnRoaXJkX2Jvcm4gPC0gZW1pc3Npb25zX3dpZGUgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGZvdXJ0aCkpICU+JSANCiAgZ3JvdXBfYnkoZm91cnRoLCB0aGlyZCwgc2Vjb25kLCBmaXJzdCkgJT4lIA0KICBzdW1tYXJpc2UodmFsdWUgPSBzdW0odmFsdWUpLCAuZ3JvdXBzID0gImRyb3BfbGFzdCIpICU+JSANCiAgbXV0YXRlKGlkID0gcGFzdGUoZmlyc3QsIHNlY29uZCwgdGhpcmQsIGZvdXJ0aCwgc2VwID0gIiAtICIpKSAlPiUgDQogIG11dGF0ZShwYXJlbnQgPSBwYXN0ZShmaXJzdCwgc2Vjb25kLCB0aGlyZCwgc2VwID0gIiAtICIpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIHNlbGVjdChpZCwNCiAgICAgICAgIGxhYmVsID0gZm91cnRoLA0KICAgICAgICAgcGFyZW50LA0KICAgICAgICAgdmFsdWUpDQpgYGANCg0KYGBge3J9DQojIHRoZSBudW1iZXIgb2YgcG90ZW50aWFsIGNoaWxkIGxheWVycyBpcyB0aGUgbWF4aW11bSBpbmNsdWRlZCBpbiB0aGUgZGF0YXNldA0Kbl9wb3RlbnRpYWxfY2hpbGRfbGF5ZXJzIDwtIGVtaXNzaW9uc19sb25nICU+JSANCiAgc2VsZWN0KGlkKSAlPiUgDQogIHB1bGwoKSAlPiUgDQogIHN0cl9jb3VudCgiIC0gIikgJT4lIA0KICBtYXgoKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmNyZWF0ZV9oaWVyYXJjaHlfZGYgPC0gZnVuY3Rpb24oZGYsIGlkX2NvbCA9ICJpZCIsIGlkX3NlcCA9ICIgLSAiKSB7DQogIG5fcG90ZW50aWFsX2NoaWxkX2xheWVycyA8LSBkZiAlPiUgDQogICAgc2VsZWN0KGNvbCkgJT4lIA0KICAgIHB1bGwoKSAlPiUgDQogICAgc3RyX2NvdW50KHNlcCkgJT4lIA0KICAgIG1heCgpDQogIA0KICANCn0NCmBgYA0KDQoNCg0KYGBge3J9DQpgYGANCg0KDQpgYGB7cn0NCmVtaXNzaW9uc19sb25nIDwtIGJpbmRfcm93cyhsaXN0KHBhcmVudHMsIGZpcnN0X2Jvcm4sIHNlY29uZF9ib3JuLCB0aGlyZF9ib3JuKSkgJT4lIA0KICBtdXRhdGUodW5pdHMgPSBnaGdfZW1pc3Npb25zX2NsZWFuJHVuaXRzWzFdKSAlPiUgDQogIHdyaXRlX2NzdigiZGF0YS9jbGVhbl9kYXRhL2hpZXJhcmNoaWNhbF9kYXRhLmNzdiIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KZW1pc3Npb25zX2xvbmcgJT4lDQogIGRhdGEuZnJhbWUoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUgDQogIHBsb3RfbHkoDQogICAgaWRzID0gfmlkLA0KICAgIGxhYmVscyA9IH5sYWJlbCwNCiAgICBwYXJlbnRzID0gfnBhcmVudCwNCiAgICB2YWx1ZXMgPSB+dmFsdWUsDQogICAgdHlwZSA9ICJ0cmVlbWFwIiwNCiAgICBicmFuY2h2YWx1ZXMgPSAidG90YWwiLA0KICAgIG1heGRlcHRoID0gMiwNCiAgICB0ZXh0aW5mbz0nbGFiZWwrcGVyY2VudCByb290K2VudHJ5Jw0KICApDQpgYGANCg0KDQpgYGB7cn0NCmZpbHRlcmVkX2doZ19oaWVyY2hhcnkgPC0gZ2hnX2hpZXJjaGFyeSAlPiUgDQogIGZpbHRlcih5ZWFyID09IDE5OTApICU+JQ0KICBmaWx0ZXIocG9sbHV0YW50ICVpbiUgYygiQ08yIiwgIk4yTyIpKQ0KDQplbWlzc2lvbnMgPC0gZmlsdGVyZWRfZ2hnX2hpZXJjaGFyeSAlPiUgDQogIGZpbHRlcighdmFsdWUgPCAwKSAlPiUgDQogIGdyb3VwX2J5KGlkLCBsYWJlbCwgcGFyZW50KSAlPiUgDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICdkcm9wX2xhc3QnKSAlPiUgDQogIG11dGF0ZSh1bml0cyA9ICJtZWdhdG9ubmVzIG9mIENPMiBlcXVpdmVsYW50IikNCg0Kc2lua3MgPC0gZmlsdGVyZWRfZ2hnX2hpZXJjaGFyeSAlPiUgDQogIGZpbHRlcih2YWx1ZSA8IDApICU+JSANCiAgbXV0YXRlKHZhbHVlID0gdmFsdWUgKiAtMSkgJT4lIA0KICBncm91cF9ieShpZCwgbGFiZWwsIHBhcmVudCkgJT4lIA0KICBzdW1tYXJpc2UodmFsdWUgPSBzdW0odmFsdWUsIG5hLnJtID0gVFJVRSksIC5ncm91cHMgPSAnZHJvcF9sYXN0JykgJT4lIA0KICBtdXRhdGUodW5pdHMgPSAibWVnYXRvbm5lcyBvZiBDTzIgZXF1aXZlbGFudCIpDQoNCmVtaXNzaW9ucyAlPiUgDQogIGRhdGEuZnJhbWUoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUgDQogIHBsb3RfbHkoDQogICAgaWRzID0gfmlkLA0KICAgIGxhYmVscyA9IH5sYWJlbCwNCiAgICBwYXJlbnRzID0gfnBhcmVudCwNCiAgICB2YWx1ZXMgPSB+dmFsdWUsDQogICAgdHlwZSA9ICJzdW5idXJzdCIsDQogICAgbWF4ZGVwdGggPSAyLA0KICAgIGluc2lkZXRleHRvcmllbnRhdGlvbiA9ICdyYWRpYWwnLA0KICAgIG1hcmtlcj1saXN0KGNvbG9yc2NhbGU9J1ZpcmlkaXMnKSwNCiAgICB0ZXh0ID0gfnVuaXRzLA0KICAgIHRleHRpbmZvPSdsYWJlbCtwZXJjZW50IHJvb3QrdmFsdWUnLA0KICAgIGhvdmVyaW5mbyA9IHBhc3RlKCIle2xhYmVsfTogPGJyPiV7dmFsdWV9IiwndGV4dCcpDQogICkNCmBgYA0KDQoNCmBgYHtyfQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KcGxvdF9seSgNCiAgbGFiZWxzID0gYygiRXZlIiwgIlNldGgiLCAiRW5vcyIsICJOb2FtIiwgIkF3YW4iLCAiRW5vY2giKSwNCiAgcGFyZW50cyA9IGMoIiIsICJFdmUiLCAiU2V0aCIsICJTZXRoIiwgIkV2ZSIsICJBd2FuIiksDQogIHZhbHVlcyA9IGMoMTYsIDEyLCAxMCwgMiwgNCwgNCksDQogIHR5cGUgPSAic3VuYnVyc3QiLA0KICBicmFuY2h2YWx1ZXMgPSAidG90YWwiDQopDQpgYGANCg0KYGBge3J9DQp0aWJibGUoDQogIGxhYmVscyA9IGMoIkV2ZSIsICJTZXRoIiwgIkVub3MiLCAiTm9hbSIsICJBd2FuIiwgIkVub2NoIiksDQogIHBhcmVudHMgPSBjKCIiLCAiRXZlIiwgIlNldGgiLCAiU2V0aCIsICJFdmUiLCAiQXdhbiIpLA0KICB2YWx1ZXMgPSBjKDE2LCAxMiwgMTAsIDIsIDQsIDQpDQopDQpgYGANCg0KYGBge3J9DQplbWlzc2lvbnNfbG9uZw0KYGBgDQoNCg0KYGBge3J9DQpmaWcgPC0gcGxvdF9seSgNCiAgbGFiZWxzID0gY2Fpbl9ibGUkbGFiZWxzLA0KICBwYXJlbnRzID0gY2Fpbl9ibGUkcGFyZW50cywNCiAgdmFsdWVzID0gY2Fpbl9ibGUkdmFsdWVzLA0KICB0eXBlID0gJ3N1bmJ1cnN0Jw0KKQ0KDQpmaWcNCmBgYA0KDQoNCmBgYHtyfQ0KZW1pc3Npb25zX2xvbmckbGFiZWxbMToxMF0NCmBgYA0KDQoNCmBgYHtyfQ0KZCA8LSBkYXRhLmZyYW1lKA0KICAgIGlkcyA9IGMoDQogICAgIk5vcnRoIEFtZXJpY2EiLCAiRXVyb3BlIiwgIkF1c3RyYWxpYSIsICJOb3J0aCBBbWVyaWNhIC0gRm9vdGJhbGwiLCAiU29jY2VyIiwNCiAgICAiTm9ydGggQW1lcmljYSAtIFJ1Z2J5IiwgIkV1cm9wZSAtIEZvb3RiYWxsIiwgIlJ1Z2J5IiwNCiAgICAiRXVyb3BlIC0gQW1lcmljYW4gRm9vdGJhbGwiLCJBdXN0cmFsaWEgLSBGb290YmFsbCIsICJBc3NvY2lhdGlvbiIsDQogICAgIkF1c3RyYWxpYW4gUnVsZXMiLCAiQXV0c3RyYWxpYSAtIEFtZXJpY2FuIEZvb3RiYWxsIiwgIkF1c3RyYWxpYSAtIFJ1Z2J5IiwNCiAgICAiUnVnYnkgTGVhZ3VlIiwgIlJ1Z2J5IFVuaW9uIg0KICApLA0KICBsYWJlbHMgPSBjKA0KICAgICJOb3J0aDxicj5BbWVyaWNhIiwgIkV1cm9wZSIsICJBdXN0cmFsaWEiLCAiRm9vdGJhbGwiLCAiU29jY2VyIiwgIlJ1Z2J5IiwNCiAgICAiRm9vdGJhbGwiLCAiUnVnYnkiLCAiQW1lcmljYW48YnI+Rm9vdGJhbGwiLCAiRm9vdGJhbGwiLCAiQXNzb2NpYXRpb24iLA0KICAgICJBdXN0cmFsaWFuPGJyPlJ1bGVzIiwgIkFtZXJpY2FuPGJyPkZvb3RiYWxsIiwgIlJ1Z2J5IiwgIlJ1Z2J5PGJyPkxlYWd1ZSIsDQogICAgIlJ1Z2J5PGJyPlVuaW9uIg0KICApLA0KICBwYXJlbnRzID0gYygNCiAgICAiIiwgIiIsICIiLCAiTm9ydGggQW1lcmljYSIsICJOb3J0aCBBbWVyaWNhIiwgIk5vcnRoIEFtZXJpY2EiLCAiRXVyb3BlIiwNCiAgICAiRXVyb3BlIiwgIkV1cm9wZSIsIkF1c3RyYWxpYSIsICJBdXN0cmFsaWEgLSBGb290YmFsbCIsICJBdXN0cmFsaWEgLSBGb290YmFsbCIsDQogICAgIkF1c3RyYWxpYSAtIEZvb3RiYWxsIiwgIkF1c3RyYWxpYSAtIEZvb3RiYWxsIiwgIkF1c3RyYWxpYSAtIFJ1Z2J5IiwNCiAgICAiQXVzdHJhbGlhIC0gUnVnYnkiDQogICksDQogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KKQ0KDQpmaWcgPC0gcGxvdF9seShkLCBpZHMgPSB+aWRzLCBsYWJlbHMgPSB+bGFiZWxzLCBwYXJlbnRzID0gfnBhcmVudHMsIHR5cGUgPSAnc3VuYnVyc3QnKQ0KDQpkDQpgYGANCg0KYGBge3J9DQpjbGFzcyhlbWlzc2lvbnNfbG9uZykNCmBgYA0KYGBge3J9DQpjbGFzcyhjYWluX2JsZSkNCmBgYA0KDQoNCmBgYHtyfQ0KZGlmZl9vcmRlciA8LSBnaGdfZW1pc3Npb25zX2NsZWFuICU+JSANCiAgZHBseXI6Omdyb3VwX2J5KGNjcF9tYXBwaW5nLCB5ZWFyLCBwb2xsdXRhbnQpICU+JSANCiAgZHBseXI6OnN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSksIHVuaXRzID0gdW5pdHNbMV0sIC5ncm91cHMgPSAia2VlcCIpICU+JQ0KICBmaWx0ZXIocG9sbHV0YW50ID09ICJDTzIiKSAlPiUgDQogIGZpbHRlcih5ZWFyID09IG1pbihnaGdfZW1pc3Npb25zX2NsZWFuJHllYXIpIHwNCiAgICAgICAgICAgeWVhciA9PSBtYXgoZ2hnX2VtaXNzaW9uc19jbGVhbiR5ZWFyKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBncm91cF9ieShjY3BfbWFwcGluZykgJT4lIA0KICBtdXRhdGUoZGlmZiA9IGxhZyh2YWx1ZSkgLSB2YWx1ZSkgJT4lIA0KICBhcnJhbmdlKGRpZmYpICU+JQ0KICBkcm9wX25hKCkgJT4lIA0KICBzZWxlY3QoY2NwX21hcHBpbmcpICU+JSANCiAgcHVsbCgpDQpgYGANCg0KDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfY2xlYW4gJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoY2NwX21hcHBpbmcsIHllYXIsIHBvbGx1dGFudCkgJT4lIA0KICBkcGx5cjo6c3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgdW5pdHMgPSB1bml0c1sxXSwgLmdyb3VwcyA9ICJrZWVwIikgJT4lDQogIGZpbHRlcihwb2xsdXRhbnQgPT0gIkNPMiIpICU+JQ0KICBtdXRhdGUoY2NwX21hcHBpbmcgPSBmYWN0b3IoY2NwX21hcHBpbmcsIGxldmVscyA9IHJldihkaWZmX29yZGVyKSkpICU+JSANCiAgZ2dwbG90KCkgKw0KICBhZXMoeCA9IHllYXIsIHkgPSB2YWx1ZSwgZmlsbCA9IGNjcF9tYXBwaW5nKSArDQogIGdlb21fYXJlYSgpICsNCiAgZmFjZXRfd3JhcCh+cG9sbHV0YW50KSArDQogIHRoZW1lX2J3KCkNCmBgYA0KYGBge3J9DQpnaGdfZW1pc3Npb25zX2NsZWFuICU+JSANCiAgZ3JvdXBfYnkocG9sbHV0YW50KSAlPiUgDQogIHN1bW1hcmlzZShlbWlzc2lvbnMgPSBzdW0odmFsdWUpKQ0KYGBgDQoNCg0KYGBge3J9DQpwbG90bHk6OmdncGxvdGx5KA0KICBnaGdfZW1pc3Npb25zX2NsZWFuICU+JSANCiAgICBmaWx0ZXIoeWVhciA9PSAyMDE4KSAlPiUNCiAgICBmaWx0ZXIocG9sbHV0YW50ICVpbiUgYygiQ08yIiwgIkNINCIpKSAlPiUNCiAgICBnZ3Bsb3QoKSArDQogICAgYWVzKHggPSBmYWN0b3IoY2NwX21hcHBpbmcsIGxldmVscyA9IHJldihsZXZlbHMoZmFjdG9yKGNjcF9tYXBwaW5nKSkpKSwNCiAgICAgICAgeSA9IHZhbHVlLCBmaWxsID0gc291cmNlX25hbWUsDQogICAgICAgIHRleHQgPSBwYXN0ZTAoJzwvYnI+IFNlY3RvcjogJywgY2NwX21hcHBpbmcsDQogICAgICAgICAgICAgICAgICAgICAgJzwvYnI+IEVtaXNzaW9uczogJywgdmFsdWUsDQogICAgICAgICAgICAgICAgICAgICAgJzwvYnI+IFNvdXJjZSBOYW1lOiAnLCBzb3VyY2VfbmFtZSkpICsNCiAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgICB0aGVtZV9idygpICsNCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgICBsYWJzKHggPSAiU2VjdG9yIiwNCiAgICAgICAgIHkgPSBwYXN0ZTAoIkVtaXNzaW9ucyAoIiwgZ2hnX2VtaXNzaW9uc19jbGVhbiR1bml0c1sxXSwgIikiKSkgKw0KICAgIGNvb3JkX2ZsaXAoKSwNCiAgICB0b29sdGlwID0gJ3RleHQnDQogICkNCg0KYGBgDQoNCg0KYGBge3J9DQpnaGdfZW1pc3Npb25zX2RhdGEgJT4lIA0KICBuYW1lcygpDQpgYGANCg0KYGBge3J9DQpnaGdfZW1pc3Npb25zX2RhdGEgJT4lIA0KICBzZWxlY3QoKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmlucHV0IDwtIGxpc3QoKQ0KDQppbnB1dCRjb2xfY2hvaWNlID0gIm5hdGlvbmFsX2NvbW11bmljYXRpb25fY2F0ZWdvcmllcyINCmBgYA0KDQoNCg0KYGBge3J9DQpnaGdfZW1pc3Npb25zX2NsZWFuICU+JQ0KICBncm91cF9ieV8oaW5wdXQkY29sX2Nob2ljZSwgImVtaXNzaW9uX3llYXIiKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbF9naGdfZW1pc3Npb25zID0gc3VtKGVtaXNzaW9ucykpICU+JSANCiAgZ2dwbG90KCkgKw0KICBhZXMoeCA9IEVtaXNzaW9uWWVhciwgeSA9IHRvdGFsX2doZ19lbWlzc2lvbnMsIGdyb3VwID0gYE5hdGlvbmFsIENvbW11bmljYXRpb24gQ2F0ZWdvcmllc2AsIGNvbG91ciA9IGBOYXRpb25hbCBDb21tdW5pY2F0aW9uIENhdGVnb3JpZXNgKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxOTkwLDIwMjAsNSkpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gMCkNCmBgYA0KDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfZGF0YSAlPiUgDQogIGRpc3RpbmN0KGBOYXRpb25hbCBDb21tdW5pY2F0aW9uIENhdGVnb3JpZXNgKQ0KYGBgDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfZGF0YSAlPiUgDQogIGZpbHRlcihFbWlzc2lvblllYXIgIT0gIkJhc2VZZWFyIikgJT4lIA0KICBtdXRhdGUoRW1pc3Npb25ZZWFyID0gYXMubnVtZXJpYyhFbWlzc2lvblllYXIpKSAlPiUgDQogIGdyb3VwX2J5KEVtaXNzaW9uWWVhcikgJT4lIA0KICBzdW1tYXJpc2UodG90YWxfZ2hnX2VtaXNzaW9ucyA9IHN1bShgRW1pc3Npb25zIChNdENPMmUpYCkpICU+JSANCiAgZ2dwbG90KCkgKw0KICBhZXMoeCA9IEVtaXNzaW9uWWVhciwgeSA9IHRvdGFsX2doZ19lbWlzc2lvbnMpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5OTAsMjAyMCw1KSkgKw0KICB5bGltKDAsIDgwKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IDApICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCg0KYGBge3J9DQpnaGdfZW1pc3Npb25zX2RhdGEgJT4lIA0KICBkaXN0aW5jdChgQ0NQIG1hcHBpbmdgKQ0KYGBgDQoNCmBgYHtyfQ0KDQpgYGANCg0KDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfZGF0YSAlPiUgDQogIGRpc3RpbmN0KGBOYXRpb25hbCBDb21tdW5pY2F0aW9uIENhdGVnb3JpZXNgKQ0KYGBgDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfZGF0YQ0KYGBgDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfZGF0YSAlPiUgDQogIGZpbHRlcihFbWlzc2lvblllYXIgIT0gIkJhc2VZZWFyIikgJT4lIA0KICBtdXRhdGUoRW1pc3Npb25ZZWFyID0gYXMubnVtZXJpYyhFbWlzc2lvblllYXIpKSAlPiUgDQogIGdyb3VwX2J5KGBDQ1AgbWFwcGluZ2AsIEVtaXNzaW9uWWVhcikgJT4lIA0KICBzdW1tYXJpc2UodG90YWxfZ2hnX2VtaXNzaW9ucyA9IHN1bShgRW1pc3Npb25zIChNdENPMmUpYCkpICU+JSANCiAgZ2dwbG90KCkgKw0KICBhZXMoeCA9IEVtaXNzaW9uWWVhciwgeSA9IHRvdGFsX2doZ19lbWlzc2lvbnMsIGdyb3VwID0gYENDUCBtYXBwaW5nYCwgY29sb3VyID0gYENDUCBtYXBwaW5nYCkgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk5MCwyMDIwLDUpKQ0KYGBgDQoNCmBgYHtyfQ0KZ2hnX2VtaXNzaW9uc19kYXRhICU+JSANCiAgZmlsdGVyKEVtaXNzaW9uWWVhciAhPSAiQmFzZVllYXIiKSAlPiUgDQogIG11dGF0ZShFbWlzc2lvblllYXIgPSBhcy5udW1lcmljKEVtaXNzaW9uWWVhcikpICU+JSANCiAgZmlsdGVyKGBOYXRpb25hbCBDb21tdW5pY2F0aW9uIENhdGVnb3JpZXNgICE9IGBDQ1AgbWFwcGluZ2ApICU+JSANCiAgc2VsZWN0KGBOYXRpb25hbCBDb21tdW5pY2F0aW9uIENhdGVnb3JpZXNgLCBgQ0NQIG1hcHBpbmdgKSAlPiUgDQogIHVuaXF1ZSgpDQpgYGANCmNhdGVnb3J5X2lkLGNhdGVnb3J5X25hbWUsc3ViY2F0ZWdvcnlfaWQsc3ViY2F0ZWdvcnlfbmFtZSx5ZWFyLGVtaXNzaW9ucyxlbWlzc2lvbg0KDQpgYGB7cn0NCmVtaXNzaW9uc19zYW5rZXkgPC0gZW1pc3Npb25zX2RhdGEgJT4lIA0KICBzZWxlY3QoY2NwX21hcHBpbmcsIHNvdXJjZV9uYW1lLCBwb2xsdXRhbnQsIGVtaXNzaW9uX3llYXIsIGVtaXNzaW9ucywgdW5pdHMpDQpgYGANCg0KDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfY2xlYW4NCmBgYA0KDQoNCmBgYHtyfQ0KZmlsdGVyZWRfZGYgPC0gZ2hnX2VtaXNzaW9uc19jbGVhbiAlPiUgDQogIHNlbGVjdChjY3BfbWFwcGluZywgc291cmNlX25hbWUsIHBvbGx1dGFudCwgZW1pc3Npb25feWVhciwgZW1pc3Npb25zLCB1bml0cykgJT4lIA0KICBmaWx0ZXIocG9sbHV0YW50ID09ICJDTzIiKSAlPiUgDQogIGZpbHRlcihlbWlzc2lvbl95ZWFyID09ICIyMDA1IikNCmBgYA0KDQpgYGB7cn0NCnRvdGFsX2VtaXNzaW9uc19mb3JfZ2FzIDwtIGZpbHRlcmVkX2RmICU+JSANCiAgc3VtbWFyaXNlKHN1bShlbWlzc2lvbnMpKSAlPiUgDQogIHB1bGwoKQ0KDQp0b3RhbF9lbWlzc2lvbnNfYnlfY2F0ZWdvcnkgPC0gZmlsdGVyZWRfZGYgJT4lIA0KICBzZWxlY3QoLXBvbGx1dGFudCkgJT4lIA0KICBncm91cF9ieShjY3BfbWFwcGluZykgJT4lIA0KICBzdW1tYXJpc2UoY2F0X3N1bSA9IHN1bShlbWlzc2lvbnMpLCAuZ3JvdXBzID0gJ2Ryb3BfbGFzdCcpDQpgYGANCg0KYGBge3J9DQpjYXRlZ29yaWVzIDwtIGZpbHRlcmVkX2RmICU+JSANCiAgZGlzdGluY3QoY2NwX21hcHBpbmcpICU+JSANCiAgcHVsbCgpDQoNCm5fY2F0ZWdvcmllcyA8LSBsZW5ndGgoY2F0ZWdvcmllcykNCg0Kc291cmNlcyA8LSBmaWx0ZXJlZF9kZiAlPiUgDQogIGRpc3RpbmN0KHNvdXJjZV9uYW1lKSAlPiUgDQogIHB1bGwoKQ0KDQpuX3NvdXJjZXMgPC0gbGVuZ3RoKHNvdXJjZXMpDQpgYGANCg0KYGBge3J9DQpuX3NvdXJjZXMNCmBgYA0KDQoNCmBgYHtyfQ0Kbm9kZV9uYW1lcyA8LSBjKCJUb3RhbCIsIGNhdGVnb3JpZXMsIHNvdXJjZXMsICJPdGhlciIpDQoNCm5vZGVfbmFtZXNfZGYgPC0gZGF0YS5mcmFtZSgibmFtZSIgPSBub2RlX25hbWVzKQ0KDQp0b3RhbF9zYW5rZXlfdGliYmxlIDwtIHRvdGFsX2VtaXNzaW9uc19ieV9jYXRlZ29yeSAlPiUNCiAgbXV0YXRlKHRvdGFsID0gIlRvdGFsIikgJT4lIA0KICBtdXRhdGUodG90YWwgPSBtYXRjaCh0b3RhbCwgbm9kZV9uYW1lcykgLTEpICU+JSANCiAgbXV0YXRlKGNjcF9tYXBwaW5nID0gbWF0Y2goY2NwX21hcHBpbmcsIG5vZGVfbmFtZXMpIC0xKSAlPiUgDQogIHNlbGVjdChzb3VyY2UgPSB0b3RhbCwNCiAgICAgICAgIHRhcmdldCA9IGNjcF9tYXBwaW5nLA0KICAgICAgICAgdmFsdWUgPSBjYXRfc3VtKQ0KDQp0b3RhbF9maWx0ZXJlZF9lbWlzc2lvbnMgPC0gdG90YWxfc2Fua2V5X3RpYmJsZSAlPiUgDQogIHN1bW1hcmlzZShzdW0odmFsdWUpKSAlPiUgDQogIHB1bGwoKQ0KDQpvdGhlcl9lbWlzc2lvbnMgPC0gdG90YWxfZW1pc3Npb25zX2Zvcl9nYXMgLSB0b3RhbF9maWx0ZXJlZF9lbWlzc2lvbnMNCg0KdG90YWxfb3RoZXJfc2Fua2V5X3RpYmJsZSA8LSB0aWJibGUoDQogICJzb3VyY2UiID0gYygwKSwNCiAgInRhcmdldCIgPSAobWF0Y2goIk90aGVyIiwgbm9kZV9uYW1lcykgLTEpLA0KICAidmFsdWUiID0gYyhvdGhlcl9lbWlzc2lvbnMpDQopDQoNCg0Kc3ViX3NhbmtleV90aWJibGUgPC0gZmlsdGVyZWRfZGYgJT4lIA0KICBzZWxlY3QoLSBjKHVuaXRzLCBwb2xsdXRhbnQsIGVtaXNzaW9uX3llYXIpKSAlPiUgDQogIG11dGF0ZShjY3BfbWFwcGluZyA9IG1hdGNoKGNjcF9tYXBwaW5nLCBub2RlX25hbWVzKSAtMSwNCiAgICAgICAgIHNvdXJjZV9uYW1lID0gbWF0Y2goc291cmNlX25hbWUsIG5vZGVfbmFtZXMpIC0xKQ0KDQpuYW1lcyhzdWJfc2Fua2V5X3RpYmJsZSkgPSBjKCJzb3VyY2UiLCAidGFyZ2V0IiwgInZhbHVlIikNCg0Kc2Fua2V5X3RpYmJsZSA8LSB0b3RhbF9zYW5rZXlfdGliYmxlICU+JSANCiAgYmluZF9yb3dzKHN1Yl9zYW5rZXlfdGliYmxlKSAlPiUgDQogIGJpbmRfcm93cyh0b3RhbF9vdGhlcl9zYW5rZXlfdGliYmxlKQ0KDQpsaW5rc19tYXRyaXggPC0gZGF0YS5mcmFtZShhcy5tYXRyaXgoc2Fua2V5X3RpYmJsZSwgYnlyb3cgPSBUUlVFLCBuY29scyA9IDMpKQ0KDQojIEFkZCBhICdncm91cCcgY29sdW1uIHRvIGVhY2ggY29ubmVjdGlvbjoNCmxpbmtzIDwtIGxpbmtzX21hdHJpeCAlPiUgDQogIG11dGF0ZShncm91cCA9IGNhc2Vfd2hlbigNCiAgICBzb3VyY2UgPT0gMCB+IHBhc3RlKCJ0eXBlXyIsIHRhcmdldCwgc2VwID0gIiIpLA0KICAgIHNvdXJjZSE9MCB+IHBhc3RlKCJ0eXBlXyIsIHNvdXJjZSwgc2VwID0gIiIpDQogICkpDQoNCm5vZGVzIDwtIG5vZGVfbmFtZXNfZGYNCiMgQWRkIGEgJ2dyb3VwJyBjb2x1bW4gdG8gZWFjaCBub2RlLg0KIyBBbGwgb2YgdGhlbSBpbiB0aGUgc2FtZSBncm91cCB0byBtYWtlIHRoZW0gdGhlIHNhbWUgY29sb3VyDQpub2RlcyRncm91cCA8LSBhcy5mYWN0b3IoYygibXlfdW5pcXVlX2dyb3VwIikpDQoNCmVtaXNzaW9ucyA8LSBsaXN0KCkNCg0KZW1pc3Npb25zJG5vZGVzIDwtIG5vZGVzDQplbWlzc2lvbnMkbGlua3MgPC0gbGlua3MNCg0KDQpgYGANCg0KDQoNCg0KDQoNCmBgYHtyfQ0KdG90YWxfZmlsdGVyZWRfZW1pc3Npb25zIDwtIHRvdGFsX3NhbmtleV90aWJibGUgJT4lIA0KICBzdW1tYXJpc2Uoc3VtKHZhbHVlKSkgJT4lIA0KICBwdWxsKCkNCg0Kb3RoZXJfZW1pc3Npb25zIDwtIHRvdGFsX2VtaXNzaW9uc19mb3JfZ2FzIC0gdG90YWxfZmlsdGVyZWRfZW1pc3Npb25zDQoNCnRvdGFsX290aGVyX3NhbmtleV90aWJibGUgPC0gdGliYmxlKA0KICAic291cmNlIiA9IGMoMCksDQogICJ0YXJnZXQiID0gKG1hdGNoKCJPdGhlciIsIG5vZGVfbmFtZXMpIC0xKSwNCiAgInZhbHVlIiA9IGMob3RoZXJfZW1pc3Npb25zKQ0KKQ0KDQpzdWJfc2Fua2V5X3RpYmJsZSA8LSBmaWx0ZXJlZF90aWJibGUgJT4lIA0KICBzZWxlY3QoLWNhdGVnb3J5X2lkLCAtc3ViY2F0ZWdvcnlfaWQsIC15ZWFyKSAlPiUgDQogIG11dGF0ZShjYXRlZ29yeV9uYW1lID0gbWF0Y2goY2F0ZWdvcnlfbmFtZSwgbm9kZV9uYW1lcykgLTEsDQogICAgICAgICBzdWJjYXRlZ29yeV9uYW1lID0gbWF0Y2goc3ViY2F0ZWdvcnlfbmFtZSwgbm9kZV9uYW1lcykgLTEpDQoNCm5hbWVzKHN1Yl9zYW5rZXlfdGliYmxlKSA9IGMoInNvdXJjZSIsICJ0YXJnZXQiLCAidmFsdWUiKQ0KDQpzYW5rZXlfdGliYmxlIDwtIHRvdGFsX3NhbmtleV90aWJibGUgJT4lIA0KICBiaW5kX3Jvd3Moc3ViX3NhbmtleV90aWJibGUpICU+JSANCiAgYmluZF9yb3dzKHRvdGFsX290aGVyX3NhbmtleV90aWJibGUpDQoNCmxpbmtzX21hdHJpeCA8LSBkYXRhLmZyYW1lKGFzLm1hdHJpeChzYW5rZXlfdGliYmxlLCBieXJvdyA9IFRSVUUsIG5jb2xzID0gMykpDQoNCiMgQWRkIGEgJ2dyb3VwJyBjb2x1bW4gdG8gZWFjaCBjb25uZWN0aW9uOg0KbGlua3MgPC0gbGlua3NfbWF0cml4ICU+JSANCiAgbXV0YXRlKGdyb3VwID0gY2FzZV93aGVuKA0KICAgIHNvdXJjZSA9PSAwIH4gcGFzdGUoInR5cGVfIiwgdGFyZ2V0LCBzZXAgPSAiIiksDQogICAgc291cmNlIT0wIH4gcGFzdGUoInR5cGVfIiwgc291cmNlLCBzZXAgPSAiIikNCiAgKSkNCg0Kbm9kZXMgPC0gbm9kZV9uYW1lc19kZg0KIyBBZGQgYSAnZ3JvdXAnIGNvbHVtbiB0byBlYWNoIG5vZGUuDQojIEFsbCBvZiB0aGVtIGluIHRoZSBzYW1lIGdyb3VwIHRvIG1ha2UgdGhlbSB0aGUgc2FtZSBjb2xvdXINCm5vZGVzJGdyb3VwIDwtIGFzLmZhY3RvcihjKCJteV91bmlxdWVfZ3JvdXAiKSkNCg0KZW1pc3Npb25zIDwtIGxpc3QoKQ0KDQplbWlzc2lvbnMkbm9kZXMgPC0gbm9kZXMNCmVtaXNzaW9ucyRsaW5rcyA8LSBsaW5rcw0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KYGBge3J9DQptYWtlX3NhbmtleV9kZnMgPC0gZnVuY3Rpb24oZGF0YSwgdXNlclllYXIsIHVzZXJHYXMpIHsNCiAgbl9jYXRlZ29yaWVzIDwtIGRhdGEgJT4lIA0KICAgIGRpc3RpbmN0KGNhdGVnb3J5X25hbWUpICU+JSANCiAgICBucm93KCkNCiAgDQogIHRvdGFsX2VtaXNzaW9uc19mb3JfZ2FzIDwtIGRhdGEgJT4lIA0KICAgIGZpbHRlcihlbWlzc2lvbiA9PSB1c2VyR2FzKCkpICU+JSANCiAgICBmaWx0ZXIoeWVhciA9PSB1c2VyWWVhcigpKSAlPiUNCiAgICBzdW1tYXJpc2Uoc3VtKGVtaXNzaW9ucykpICU+JSANCiAgICBwdWxsKCkNCiAgDQogIGZpbHRlcmVkX3RpYmJsZSA8LSBkYXRhICU+JQ0KICAgIGZpbHRlcihlbWlzc2lvbiA9PSB1c2VyR2FzKCkpICU+JSANCiAgICBzZWxlY3QoLWVtaXNzaW9uKSAlPiUgDQogICAgZmlsdGVyKHllYXIgPT0gdXNlclllYXIoKSkgJT4lIA0KICAgIGZpbHRlcihlbWlzc2lvbnMgPiB1c2VyUmVzb2x1dGlvbigpKQ0KICANCiAgdG90YWxfZW1pc3Npb25zX2J5X2NhdCA8LSBmaWx0ZXJlZF90aWJibGUgJT4lDQogICAgZ3JvdXBfYnkoY2F0ZWdvcnlfbmFtZSkgJT4lIA0KICAgIHN1bW1hcmlzZShjYXRfc3VtID0gc3VtKGVtaXNzaW9ucyksIC5ncm91cHMgPSAnZHJvcF9sYXN0JykNCiAgDQogIGNhdGVnb3JpZXMgPC0gZmlsdGVyZWRfdGliYmxlICU+JQ0KICAgIGRpc3RpbmN0KGNhdGVnb3J5X25hbWUpICU+JSANCiAgICBwdWxsKCkNCiAgDQogIHN1YmNhdGVnb3JpZXMgPC0gZmlsdGVyZWRfdGliYmxlICU+JQ0KICAgIGRpc3RpbmN0KHN1YmNhdGVnb3J5X25hbWUpICU+JSANCiAgICBwdWxsKCkNCiAgDQogIG5vZGVfbmFtZXMgPC0gYygiVG90YWwiLCBjYXRlZ29yaWVzLCBzdWJjYXRlZ29yaWVzLCAiT3RoZXIiKQ0KICANCiAgbm9kZV9uYW1lc19kZiA8LSBkYXRhLmZyYW1lKCJuYW1lIiA9IG5vZGVfbmFtZXMpDQogIA0KICB0b3RhbF9zYW5rZXlfdGliYmxlIDwtIHRvdGFsX2VtaXNzaW9uc19ieV9jYXQgJT4lDQogICAgbXV0YXRlKHRvdGFsID0gIlRvdGFsIikgJT4lIA0KICAgIG11dGF0ZSh0b3RhbCA9IG1hdGNoKHRvdGFsLCBub2RlX25hbWVzKSAtMSkgJT4lIA0KICAgIG11dGF0ZShjYXRlZ29yeV9uYW1lID0gbWF0Y2goY2F0ZWdvcnlfbmFtZSwgbm9kZV9uYW1lcykgLTEpICU+JSANCiAgICBzZWxlY3Qoc291cmNlID0gdG90YWwsDQogICAgICAgICAgIHRhcmdldCA9IGNhdGVnb3J5X25hbWUsDQogICAgICAgICAgIHZhbHVlID0gY2F0X3N1bSkNCiAgDQogIHRvdGFsX2ZpbHRlcmVkX2VtaXNzaW9ucyA8LSB0b3RhbF9zYW5rZXlfdGliYmxlICU+JSANCiAgICBzdW1tYXJpc2Uoc3VtKHZhbHVlKSkgJT4lIA0KICAgIHB1bGwoKQ0KICANCiAgb3RoZXJfZW1pc3Npb25zIDwtIHRvdGFsX2VtaXNzaW9uc19mb3JfZ2FzIC0gdG90YWxfZmlsdGVyZWRfZW1pc3Npb25zDQogIA0KICB0b3RhbF9vdGhlcl9zYW5rZXlfdGliYmxlIDwtIHRpYmJsZSgNCiAgICAic291cmNlIiA9IGMoMCksDQogICAgInRhcmdldCIgPSAobWF0Y2goIk90aGVyIiwgbm9kZV9uYW1lcykgLTEpLA0KICAgICJ2YWx1ZSIgPSBjKG90aGVyX2VtaXNzaW9ucykNCiAgKQ0KICANCiAgc3ViX3NhbmtleV90aWJibGUgPC0gZmlsdGVyZWRfdGliYmxlICU+JSANCiAgICBzZWxlY3QoLWNhdGVnb3J5X2lkLCAtc3ViY2F0ZWdvcnlfaWQsIC15ZWFyKSAlPiUgDQogICAgbXV0YXRlKGNhdGVnb3J5X25hbWUgPSBtYXRjaChjYXRlZ29yeV9uYW1lLCBub2RlX25hbWVzKSAtMSwNCiAgICAgICAgICAgc3ViY2F0ZWdvcnlfbmFtZSA9IG1hdGNoKHN1YmNhdGVnb3J5X25hbWUsIG5vZGVfbmFtZXMpIC0xKQ0KICANCiAgbmFtZXMoc3ViX3NhbmtleV90aWJibGUpID0gYygic291cmNlIiwgInRhcmdldCIsICJ2YWx1ZSIpDQogIA0KICBzYW5rZXlfdGliYmxlIDwtIHRvdGFsX3NhbmtleV90aWJibGUgJT4lIA0KICAgIGJpbmRfcm93cyhzdWJfc2Fua2V5X3RpYmJsZSkgJT4lIA0KICAgIGJpbmRfcm93cyh0b3RhbF9vdGhlcl9zYW5rZXlfdGliYmxlKQ0KICANCiAgbGlua3NfbWF0cml4IDwtIGRhdGEuZnJhbWUoYXMubWF0cml4KHNhbmtleV90aWJibGUsIGJ5cm93ID0gVFJVRSwgbmNvbHMgPSAzKSkNCiAgDQogICMgQWRkIGEgJ2dyb3VwJyBjb2x1bW4gdG8gZWFjaCBjb25uZWN0aW9uOg0KICBsaW5rcyA8LSBsaW5rc19tYXRyaXggJT4lIA0KICAgIG11dGF0ZShncm91cCA9IGNhc2Vfd2hlbigNCiAgICAgIHNvdXJjZSA9PSAwIH4gcGFzdGUoInR5cGVfIiwgdGFyZ2V0LCBzZXAgPSAiIiksDQogICAgICBzb3VyY2UhPTAgfiBwYXN0ZSgidHlwZV8iLCBzb3VyY2UsIHNlcCA9ICIiKQ0KICAgICkpDQogIA0KICBub2RlcyA8LSBub2RlX25hbWVzX2RmDQogICMgQWRkIGEgJ2dyb3VwJyBjb2x1bW4gdG8gZWFjaCBub2RlLg0KICAjIEFsbCBvZiB0aGVtIGluIHRoZSBzYW1lIGdyb3VwIHRvIG1ha2UgdGhlbSB0aGUgc2FtZSBjb2xvdXINCiAgbm9kZXMkZ3JvdXAgPC0gYXMuZmFjdG9yKGMoIm15X3VuaXF1ZV9ncm91cCIpKQ0KICANCiAgZW1pc3Npb25zIDwtIGxpc3QoKQ0KICANCiAgZW1pc3Npb25zJG5vZGVzIDwtIG5vZGVzDQogIGVtaXNzaW9ucyRsaW5rcyA8LSBsaW5rcw0KICANCiAgcmV0dXJuKGVtaXNzaW9ucykNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KbWFrZV9zYW5rZXlfZGZzKGVtaXNzaW9uc19zYW5rZXksIHVzZXJZZWFyID0gMjAwNSwgdXNlckdhcyA9ICJDSDQiLCB1c2VyUmVzb2x1dGlvbiA9IDUwKQ0KYGBgDQpgYGB7cn0NCg0KYGBgDQoNCg==